package com.youtills.core.web;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: ravi
 * Date: 8/15/12
 * Time: 8:09 PM
 * To change this template use File | Settings | File Templates.
 */
public class HttpUtil {

    public static final String UTF_8 = "UTF-8";

    public static String generateUrl(String serverName, String contextName, String uri, boolean isSecure){
        return generateUrl(serverName, contextName, uri, isSecure, null);
    }

    public static String generateUrl(String serverName, String contextName, String uri, boolean isSecure, Map<String, String> params){

        String pattern = "http{0}://{1}{2}{3}";

        String https = isSecure ? "s" : "";
        String server = nonNull(serverName);
        String context = nonNull(contextName);
        String path = nonNull(uri);

        StringBuilder url = new StringBuilder(MessageFormat.format(pattern, https, server, context, path));

        if(params != null && params.size() > 0){
            if(url.indexOf("?") == -1){
                url.append("?");
            }else{
                url.append("&");
            }
            boolean needAnd = false;
            for(String key : params.keySet()){
                try {
                    if(needAnd){
                        url.append("&");
                    }else{
                        needAnd = true;
                    }
                    url.append(URLEncoder.encode(key, UTF_8));
                    url.append("=");
                    url.append(URLEncoder.encode(params.get(key), UTF_8));
                } catch (UnsupportedEncodingException e) {
                    System.err.print("System doesn't support " + UTF_8 + " encoding");
                    e.printStackTrace(System.err);
                }
            }
        }
        return url.toString();
    }

    private static String nonNull(String text){
        return text == null ? "" : text.trim();
    }

    /**
     * Method deleteHttpCookie - sets max age for cookie to -1.
     *
     * @param  response    httpresponse.
     * @param  cookieName  name of the cookie.
     */
    public static void deleteCookie(HttpServletResponse response,
            String cookieName) {
        setCookie(response,cookieName, "", 0);
    }

    /**
     * Gets cookie from given request object.!
     * This method will return a copy of the Cookie in the request, where its value has been decoded using UTF-8 encoding.
     * This method should be used in conjuction with setHttpCookie() above.
     *
     * @param   request     request to find cookie
     * @param   cookieName  cookiename to get
     *
     * @return  Cookie object if exists or null
     */
    public static Cookie getCookie(HttpServletRequest request,
            String cookieName) {

        if ((request != null) && (cookieName != null)) {
            Cookie[] cookies = request.getCookies();

            if (cookies != null) {

                for (Cookie cookie : cookies) {

                    if (cookieName.equals(cookie.getName())) {
                        return cookie;
                    }
                }
            }
        }

        return null;
    }

    public static String getCookieValue(HttpServletRequest request, String cookieName){
        Cookie cookie = getCookie(request, cookieName);
        if(cookie != null){
            String value = cookie.getValue();
            try {
                return URLDecoder.decode(value, UTF_8);
            } catch (UnsupportedEncodingException e) {
                //ignore - this should never happen...
            }
        }
        return null;
    }

    /**
     * Sets http cookie intro response object. Always sets it to maximum age and at
     * the root level so it can be accessible for the whole site. if one of the
     * params is null, does nothing.
     *
     * Note: please use getHttpCookie() in conjuction with this.
     *
     * @param  response    http response.
     * @param  cookieName  cookie name.
     * @param  value       cookie value. This value will be UTF-8 encoded before setting to the request header,
     *                     so be sure to use getHttpCookie() to retrieve the Cookie instance.
     * @param  age         - how long should the cookie live in the browser, in
     *                     seconds.
     */
    public static void setCookie(HttpServletResponse response, String cookieName,
            String value, int age) {
        setCookie(response, cookieName, value, age, null);
    }

    /**
     * Sets http cookie intro response object. Always sets it to maximum age and at
     * the root level so it can be accessible for the whole site. if one of the
     * params is null, does nothing.
     *
     * Note: please use getHttpCookie() in conjuction with this.
     *
     * @param  response    http response.
     * @param  cookieName  cookie name.
     * @param  value       cookie value. This value will be UTF-8 encoded before setting to the request header,
     *                     so be sure to use getHttpCookie() to retrieve the Cookie instance.
     * @param  age         - how long should the cookie live in the browser, in
     *                     seconds.
     * @param  domain      optional domain
     */
    public static void setCookie(HttpServletResponse response, String cookieName,
            String value, int age, String domain) {

        if ((response == null) || (cookieName == null) || (value == null)) {
            return;
        }

        try {
            Cookie portalCookie = new Cookie(cookieName,
                    URLEncoder.encode(value, UTF_8));
            portalCookie.setMaxAge(age);
            if (domain != null) {
                portalCookie.setDomain(domain);
            }
            portalCookie.setPath("/"); // force cookie to be at root level so the whole site can see it

            response.addCookie(portalCookie);

        } catch (UnsupportedEncodingException thisNeverHappensReally) {
            // and if UTF-8 ever fail here, I'll buy you a beer!
        }
    }

    public static void setSessionCookie(HttpServletResponse resposne, String cookieName, String value){
        setCookie(resposne, cookieName, value, -1);
    }

    public static void setSessionCookie(HttpServletResponse resposne, String cookieName, String value, String domain){
        setCookie(resposne, cookieName, value, -1, domain);
    }

    /**
     * Set's http session's attribute. If session does not exist, creates it. If one
     * of the passed params is null, does nothing.
     * if the value is null then its same as remove attribute.
     *
     * @param  request    - http request.
     * @param  name   session attribute to set.
     * @param  value  of attribute.
     */
    public static void setSessionAttribute(HttpServletRequest request, String name,
            Object value) {

        if ((request != null) && (name != null)) {
            HttpSession s = request.getSession(true);
            s.setAttribute(name, value);
        }
    }


    /**
     * returns http session attribute. if one of the parameters is null, returns
     * null.
     *
     * @param   request   http request.
     * @param   name  attribute.
     *
     * @return  Object value.
     */
    public static Object getSessionAttribute(HttpServletRequest request, String name) {

        if ((request == null) || (request.getSession() == null) || (name == null)) {
            return null;
        } else {
            return request.getSession().getAttribute(name);
        }
    }

    /** Default Connection Timeout Milliseconds to get response from external url. */
    private static final int REQUEST_CONNECTION_TIME_OUT_MILLIS = 5000;

    /** Default Connection Read Timeout Milliseconds to get resposne from external url. */
    private static final int READ_TIME_OUT_MILLIS = 5000;

    public static String getResponse(String requestUrl, Map<String, String> params) throws IOException {
        return getResponse(requestUrl, params, REQUEST_CONNECTION_TIME_OUT_MILLIS, READ_TIME_OUT_MILLIS);
    }


    public static String getResponse(String requestUrl, Map<String, String> params, int connectionTimeOut, int readTimeOut) throws IOException {

        StringBuilder response = new StringBuilder();

        StringBuilder urlParams = null;

        if ((params != null) && (params.size() > 0)) {
            urlParams = new StringBuilder();

            for (String paramName : params.keySet()) {
                urlParams.append(paramName);
                urlParams.append("=");
                urlParams.append(params.get(paramName));

                // Adding & at the end of the last params doesn't hurt.
                // No need to add condition to check whether to add & or not
                urlParams.append("&");
            }
        }

        URL url = new URL(requestUrl);


        URLConnection urlConnection = url.openConnection();
        urlConnection.setDoInput(true);
        urlConnection.setUseCaches(false);
        urlConnection.setConnectTimeout(connectionTimeOut);
        urlConnection.setReadTimeout(readTimeOut);


        if (urlParams != null) {

            urlConnection.setDoOutput(true);

            DataOutputStream stream = new DataOutputStream(
                    urlConnection.getOutputStream());
            stream.writeBytes(urlParams.toString());
            stream.flush();
            stream.close();
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(
                    urlConnection.getInputStream()));

        String responseContent;
        boolean needNewLine = false;

        while (null != (responseContent = reader.readLine())) {

            if (needNewLine) {
                response.append("\n");
            }

            response.append(responseContent);
            needNewLine = true;
        }

        reader.close();

        return response.toString();
    }

    public static String urlEncode(String url){

        try {
            return URLEncoder.encode(url, UTF_8);
        } catch (UnsupportedEncodingException e) {
            return url;
        }
    }

}
